-module(xbt_ctl).

-behaviour(gen_server).

%% funs for client part
-export([stop/0,stop/1]).

%% funs for server part
-export([init/1,handle_call/3,handle_cast/2,
         handle_info/2,terminate/2,code_change/3]).
-export([start_link/0]).

-include_lib("kernel/include/file.hrl").
-include("xbt.hrl").

%% the following functions are used by client.

% stop xbay table daemon
stop()  ->
   call_cmd(stop).

stop([_]) ->
   call_cmd(stop).

call_cmd(Cmd) ->
   {ok,Socket,Key} = connect(),
   send_cmd(Socket,Key,Cmd),
   init:stop().
   
connect()     ->
   case file:consult(?CFILE) of
       {ok,[{Port,Key}]}    ->
           case gen_tcp:connect({127,0,0,1},Port,[binary,{active,false},
                                                         {reuseaddr,true},
                                                         {packet,2}],
                                3000) of
               {ok, Socket}       ->
                   {ok,Socket,Key};
               {error, Reason}    ->
                   ?DEBUG("[~p]gen_tcp connect error:~p~n",[self(),Reason]),
                   exit(Reason)
           end;
       {error,Reason}       ->
           ?DEBUG("[~p]file:consule error:~p~n",[self(),Reason]),
           exit(Reason)
   end.

send_cmd(Socket,Key,Cmd)    ->
   gen_tcp:send(Socket,term_to_binary({Cmd,Key})),
   case gen_tcp:recv(Socket,0) of
       {ok,Pack} ->
            io_lib:format("~p~n",[Pack]);
       {error,Reason} ->
            ?DEBUG("[~p]gen_tcp recv error:~p~n",[self(),Reason]),
            exit(Reason)
   end,
   gen_tcp:close(Socket).

%% the following functions are used by server.

start_link()           ->
    gen_server:start_link({local,?MODULE},?MODULE,[],[]).

init([])  ->
    case gen_tcp:listen(0,[binary,
                           {packet,2},
                           {active,false},
                           {ip,{127,0,0,1}},
                           {reuseaddr,true}]) of
        {ok, Listen}         ->
            {ok,Port} = inet:port(Listen),
            {A1,A2,A3} = now(),
            random:seed(A1, A2, A3),
            Key = random:uniform(16#FFFFFFF),
            write_file(Port,Key),
            spawn_link(fun() -> 
                ctl_loop(self(),Listen,Key) end),
            {ok,{}};
        Error                ->
            ?DEBUG("[~p]Can't listen to control port, ~p~n",[self(),Error]),
            exit(Error)
    end.

terminate(_, _State) ->
    ok.

handle_cast(_Msg, State) ->
    {noreply,State}.

handle_call(_Msg,_From,State) ->
    {reply,ok,State}.

handle_info(_Info, State) ->
    {noreply, State}.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

write_file(Port,Key)  ->
    case catch begin
        filelib:ensure_dir(?CFILE),
        file:write_file(?CFILE,io_lib:format("~w.",[{Port,Key}])),
        {ok,Info} = file:read_file_info(?CFILE),
        ok = file:write_file_info(?CFILE,Info#file_info{mode = 8#00600})
    end of
        {'EXIT',Error} ->
            exit(Error);
        _              ->
            ok
    end.

ctl_loop(Parent,Listen,Key)   ->
    case gen_tcp:accept(Listen) of
        {ok,Socket}     ->
            ?DEBUG("[~p]accept new connection: ~p~n",[self(),Socket]),
            conn_entry(Parent,Socket,Key),
            ctl_loop(Parent,Listen,Key);
        _Error          ->
            ?DEBUG("[~p]accept error:~p~n",[self(),_Error]),
            timer:sleep(1000),
            ctl_loop(Parent,Listen,Key)
    end.

conn_entry(Parent,Socket,Key)  ->
    case gen_tcp:recv(Socket,0) of
        {ok, Pack}     ->
            ?DEBUG("[~p]got packet: ~p~n",[self(),Pack]),
            Cmd = (catch binary_to_term(Pack)),
            ?DEBUG("[~p]got cmd: ~p~n",[self(),Cmd]),
            do_cmd(Parent,Socket,Key,Cmd);
        {error,_Reason} ->
            ?DEBUG("[~p]server recv error:~p~n",[self,_Reason]),
            gen_tcp:close(Socket)
    end.

do_cmd(_Parent,Socket,Key,{stop,Key})  ->
    ?DEBUG("[~p]do_cmd, stop, key is ~p~n",[self(),Key]),
    gen_tcp:send(Socket,io_lib:format("[~p]stopping xbaytable~n",[self()])),
    file:delete(?CFILE),
    init:stop();

do_cmd(_Parent,Socket,_Key,Cmd)         ->
    gen_tcp:send(Socket,io_lib:format("[~p]unkowning cmd:~p~n",[self(),Cmd])),
    gen_tcp:close(Socket).
    

